package maquinaP;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;


public class MaquinaP {
	
	private Memoria memoria;
	private MemoriaInstrucciones memoriaI;
	private Pila pila;
	private String instruccion;
	private boolean p;
	private int cProg;
	private boolean modoTraza;//si es true activado modo traza si no modo normal
	
	public MaquinaP(Reader input, boolean modoTraza) throws IOException{
		memoria = new Memoria();
		memoriaI = new MemoriaInstrucciones(input);
		pila = new Pila();
		p = false;
		instruccion = "";
		cProg = 0;
		this.modoTraza = modoTraza;
	}
	
	public void procesa() throws Exception{
		do{
			siguienteInstruccion();
			procesaInstruccion();
			
			if(modoTraza){
				System.out.println("\nPulsar ENTER para continuar...");
				Reader entTeclado = new InputStreamReader(System.in);
				BufferedReader bEntTeclado = new BufferedReader(entTeclado);
				bEntTeclado.readLine();	
				
				imprime();				
			}
			
		}while (!p);
	}

	private void procesaInstruccion() throws Exception {
		String[] aux = quitaParentesis(instruccion);
		if(aux[0].equals("stop")) p = true;
		else if (aux[0].equals("apila")) procesaApila(aux[1]);
		else if (aux[0].equals("apila_dir")) procesaApilaDir(aux[1]);
		else if (aux[0].equals("desapila_dir")) procesaDesapilaDir(aux[1]);
		else if (aux[0].equals("+")) procesaSuma();
		else if (aux[0].equals("-")) procesaResta();
		else if (aux[0].equals("*")) procesaMultiplica();
		else if (aux[0].equals("/")) procesaDivide();
		else if (aux[0].equals("<")) procesaMenor();
		else if (aux[0].equals(">")) procesaMayor();
		else if (aux[0].equals("<=")) procesaMenorIgual();
		else if (aux[0].equals(">=")) procesaMayorIgual();
		else if (aux[0].equals("==")) procesaIgualIgual();
		else if (aux[0].equals("!=")) procesaDistinto();
		else if (aux[0].equals("%")) procesaModulo();
		else if (aux[0].equals("y")) procesaY();
		else if (aux[0].equals("o")) procesaO();
		else if (aux[0].equals("no")) procesaNo();
		else if (aux[0].equals("cSigno")) procesaCSigno();
		else if (aux[0].equals("<<")) procesaDesplIzquierda();
		else if (aux[0].equals(">>")) procesaDesplDerecha();
		else if (aux[0].equals("castNat")) procesaCastNat();
		else if (aux[0].equals("castEnt")) procesaCastEnt();
		else if (aux[0].equals("castReal")) procesaCastReal();
		else if (aux[0].equals("castCar")) procesaCastCar();
		else if (aux[0].equals("escribe")) procesaEscribe();
		
		//2Cuatri
		else if (aux[0].equals("desapila")) procesaDesapila();
		else if (aux[0].equals("apila_ind")) procesaApilaInd();
		else if (aux[0].equals("desapila_ind")) procesaDesapilaInd();
		else if (aux[0].equals("mueve")) procesaMueve(aux[1]);
		else if (aux[0].equals("new")) procesaNew(aux[1]);
		else if (aux[0].equals("del")) procesaDel(aux[1]);
		else if (aux[0].equals("inicio")) procesaInicio(aux[1]);
		else if (aux[0].equals("apila_ret")) procesaApilaRet(aux[1]);
		else if (aux[0].equals("prologo")) procesaPrologo(aux[1]);
		else if (aux[0].equals("apila_dis")) procesaApilaDis(aux[1]);
		else if (aux[0].equals("epilogo")) procesaEpilogo(aux[1]);
		else if (aux[0].equals("ir_ind")) procesaIrInd();
		else if (aux[0].equals("inicio_paso")) procesaInicioPaso();
		else if (aux[0].equals("fin_paso")) procesaFinPaso();
		else if (aux[0].equals("direccion_par_formal")) procesaDireccionParFormal(aux[1]);
		else if (aux[0].equals("ir_a")) procesaIrA(aux[1]);
		else if (aux[0].equals("ir_f")) procesaIrF(aux[1]);
		else if (aux[0].equals("ir_v")) procesaIrV(aux[1]);
		else if (aux[0].equals("copia")) procesaCopia();
		else if (aux[0].equals("mayor")) procesaMayorAux();
		else if (aux[0].equals("lee")) procesaLee();
		
		else throw new Exception("No se reconoce " + instruccion);
	}

	private void procesaDesapila() {
		pila.poncPila(pila.damecPila() - 1);
		cProg++;
	}

	private void procesaApilaInd() throws Exception {
		String num = (pila.damePila(pila.damecPila())).dameValor();
		if (memoria.dameMemoria(num)==null) throw new Exception("Error en ejecucion: Acceso a registro no inicializado en Memoria");	
		pila.ponPila(pila.damecPila(), memoria.dameMemoria(num));
		cProg ++;		
	}
	
	private void procesaDesapilaInd() throws Exception {
		Registro r1 = (pila.damePila(pila.damecPila()));
		String num = (pila.damePila(pila.damecPila()-1)).dameValor();
		
		if (r1==null) throw new Exception("Error en ejecucion: Acceso a registro no inicializado en Pila");
		memoria.ponMemoria(num, r1);
		pila.poncPila(pila.damecPila() - 2);
		cProg ++;
	}
	
	private void procesaMueve(String n) throws Exception {
		String numOrigen = (pila.damePila(pila.damecPila())).dameValor();
		String numDestino = (pila.damePila(pila.damecPila()-1)).dameValor();
		int numOrigenAux = Integer.parseInt(numOrigen);
		int numDestinoAux = Integer.parseInt(numDestino);
		
		int nAux = Integer.parseInt(n);
		for(int i = 0; i < nAux; i++ ){
			
			numOrigen = String.valueOf(numOrigenAux + i);
			numDestino = String.valueOf(numDestinoAux + i);
			if (memoria.dameMemoria(numOrigen)==null) throw new Exception("Error en ejecucion: Acceso a registro no inicializado en Memoria");
			Registro r1 = memoria.dameMemoria(numOrigen);
			memoria.ponMemoria(numDestino, r1);
		}
		cProg ++;
	}

	private void procesaNew(String t) {
		int num = Integer.parseInt(t);
		int pos = Memoria.dameTam()-1; 
		while(!dameEspacio(num, pos)&&(pos > Integer.parseInt(memoria.dameCP()))){
			//comprobamos el espacio
			pos--;
		}
		pila.poncPila(pila.damecPila()+ 1);
		pila.ponPila(pila.damecPila(),new Registro(String.valueOf(pos-num+1),"tNatural"));
		cProg++;
	}
	
	private boolean dameEspacio(int num, int pos){
		int numAux = 0;
		int i = pos;
		while(numAux != num && i > Integer.parseInt(memoria.dameCP())){
				if(!memoria.dameMemoria(String.valueOf(i)).equals(new Registro())) return false;
				else memoria.ponMemoria(String.valueOf(i), new Registro("nulo", "tNulo"));
				i--;
				numAux ++;
		}
		if(numAux == num)return true;
		else return false;
	}
	
	private void procesaDel(String t) throws Exception{
		if((pila.damePila(pila.damecPila())).dameValor().equals(""))throw new Exception("Liberacion de memoria no reservada anteriormente");
		int aux = Integer.parseInt((pila.damePila(pila.damecPila())).dameValor());
		for(int i = aux; i<aux+Integer.parseInt(t); i++){
			memoria.ponMemoria(String.valueOf(i), new Registro());
		}
		pila.poncPila(pila.damecPila()-1);
		cProg++;
	}

	private void procesaInicio(String variables) throws Exception {
		String[] var = variables.split(",");
		
		int varInt0 = Integer.parseInt(var[0]); // numero de niveles
		int varInt1 = Integer.parseInt(var[1]); // espacio para los niveles
		
		memoria.ponCP(varInt1+varInt0+1);
		
		for(int i = 0; i<=varInt0; i++){
			if (i==0)
				memoria.ponDisplay(i, 0);
			else memoria.ponDisplay(i,varInt0+2);
		}			
		cProg++;
	}

	
	private void procesaApilaRet(String ret) throws Exception {
		memoria.ponMemoria(String.valueOf(Integer.parseInt(memoria.dameCP())+1), new Registro(ret, "tNatural"));
		cProg++;
	}
	
	private void procesaPrologo(String variables) throws Exception {
		String[] var = variables.split(",");
		
		int nivel = Integer.parseInt(var[0]);
		int tamLocales = Integer.parseInt(var[1]);
		
		memoria.ponMemoria(String.valueOf(Integer.parseInt(memoria.dameCP())+2), new Registro(memoria.dameDisplay(nivel), "tNatural"));
		memoria.ponDisplay(nivel, Integer.parseInt(memoria.dameCP())+3);
		memoria.ponCP(Integer.parseInt(memoria.dameCP())+tamLocales+2);
		cProg++;
	}
	
	private void procesaApilaDis(String s) {
		pila.poncPila(pila.damecPila()+1);
		pila.ponPila(pila.damecPila(), new Registro(memoria.dameDisplay(Integer.parseInt(s)),"tNatural"));
		cProg ++;
	}
	
	private void procesaEpilogo(String nivelAux) throws Exception {
		int nivel = Integer.parseInt(nivelAux); 
		pila.ponPila(pila.damecPila(), memoria.dameMemoria(String.valueOf(Integer.parseInt(memoria.dameDisplay(nivel))-2)));
		memoria.ponCP(Integer.parseInt(memoria.dameDisplay(nivel))-3);
		memoria.ponDisplay(nivel, (Integer.parseInt(memoria.dameDisplay(nivel))-3)+2);
		cProg++;
	}

	private void procesaIrInd() {
		String numAux = (pila.damePila(pila.damecPila())).dameValor();
		int num = Integer.parseInt(numAux);
		pila.poncPila(pila.damecPila() - 1);
		cProg = num;
	}

	private void procesaInicioPaso() throws Exception {
		pila.poncPila(pila.damecPila()+1);
		pila.ponPila(pila.damecPila(), new Registro(String.valueOf(Integer.parseInt(memoria.dameCP())+3),"tNatural"));
		cProg ++;
	}
	
	private void procesaFinPaso() {
		procesaDesapila();
	}

	private void procesaDireccionParFormal(String pFormal) {
		procesaApila(pFormal);
	}
	
	private void procesaIrA(String i) {
		cProg = Integer.parseInt(i);
	}

	private void procesaIrF(String i) {
		String numAux = (pila.damePila(pila.damecPila())).dameValor();		
		
		if(numAux.equals("falso")) procesaIrA(i);
		else cProg++;
		
		pila.poncPila(pila.damecPila() - 1);
	}
	
	private void procesaIrV(String i) {
		String numAux = (pila.damePila(pila.damecPila())).dameValor();		
		
		if(numAux.equals("verdadero")) procesaIrA(i);
		else cProg++;
		
		pila.poncPila(pila.damecPila() - 1);
	}
	
	private void procesaCopia() {
		Registro r1 = (pila.damePila(pila.damecPila()));
		pila.poncPila(pila.damecPila() + 1);
		
		pila.ponPila(pila.damecPila(), r1);
		cProg++;	
	}

	private void procesaMayorAux() {		
		Registro r1 = new Registro(pila.damePila(pila.damecPila())); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()-1));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "mayor");
		Registro resultado = mayor(r1.dameValor(),r2.dameValor(), tipoResultado);
		//En resultado tenemos un registro con verdadero si r1 > r2, y falso en caso contrario
		
		Registro rAux = null;
		if ((resultado.dameValor()).equals("verdadero"))//Pila[CPila] > Pila[CPila -1]
				rAux = new Registro("falso","tBooleano");
		else rAux = new Registro("verdadero","tBooleano");
		
		pila.ponPila(pila.damecPila()-1, rAux);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;			
	}
		
	private void procesaLee() throws IOException {	
		Reader entTeclado2 = new InputStreamReader(System.in);
		BufferedReader bEntTeclado2 = new BufferedReader(entTeclado2);
		boolean valido = false;
		String linea="";
		
		while(!valido){
			System.out.print("\nIntroduzca un valor para la instruccion \"lee\": ");
			linea = bEntTeclado2.readLine();
			if(!linea.equals("")) valido =true;	
		}
		
		//Pasando por la pila
		pila.poncPila(pila.damecPila()+1);
		String tipo = dameTipo(linea);
		Registro aux = new Registro(linea, tipo);
		
		pila.ponPila(pila.damecPila(), aux);
		
		memoria.ponMemoria(pila.damePila(pila.damecPila()-1).dameValor(), pila.damePila(pila.damecPila()));
		pila.poncPila(pila.damecPila()-2);
		
		cProg ++;
	}
	
	private String[] quitaParentesis(String instruccion2) {
		int pos= 0;
		String respuesta[] = new String[2];
		respuesta[0] = "";
		respuesta[1] = "";
		
		for(int i=0; i<instruccion2.length();i++){
			char car = instruccion2.charAt(i);			
			if ((car == '(') || (car==')')) pos++;
			else respuesta[pos] = respuesta[pos] + instruccion2.charAt(i); 
		}
		return respuesta;
	}

	private void procesaApila(String num) {
		String tipo = dameTipo(num);
		Registro r = new Registro(num, tipo);
		pila.poncPila(pila.damecPila()+1);
		pila.ponPila(pila.damecPila(), r);
		cProg ++;
	}

	private void procesaApilaDir(String num) throws Exception {
		if (memoria.dameMemoria(num)==null) throw new Exception("Error en ejecucion: Acceso a registro no inicializado en Memoria");	
		pila.poncPila(pila.damecPila()+1);
		pila.ponPila(pila.damecPila(), memoria.dameMemoria(num));
		cProg ++;
	}

	private void procesaDesapilaDir(String num) throws Exception {
		if (pila.damePila(pila.damecPila())==null) throw new Exception("Error en ejecucion: Acceso a registro no inicializado en Pila");
		memoria.ponMemoria(num, pila.damePila(pila.damecPila()));
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private void procesaSuma() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "suma");
		Registro resultado = suma(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro suma(String v1, String v2,String tipoResultado) {
		if(tipoResultado.equals("tNatural")||tipoResultado.equals("tEntero")){
			int res = Integer.valueOf(v1) + Integer.valueOf(v2);
			return new Registro(Integer.toString(res),tipoResultado);
		}
		else {
			double res = Double.parseDouble(v1) + Double.parseDouble(v2); 
			return new Registro(Double.toString(res),tipoResultado);
		}		
	}
	
	private void procesaResta() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "resta");
		Registro resultado = resta(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro resta(String v1, String v2, String tipoResultado) {
		if(tipoResultado.equals("tNatural")||tipoResultado.equals("tEntero")){
			int res = Integer.valueOf(v1) - Integer.valueOf(v2);
			return new Registro(Integer.toString(res),tipoResultado);
		}
		else {
			double res = Double.parseDouble(v1) - Double.parseDouble(v2); 
			return new Registro(Double.toString(res),tipoResultado);
		}
	}

	private void procesaMultiplica() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "multiplica");
		Registro resultado = multiplica(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro multiplica(String v1, String v2, String tipoResultado) {
		if(tipoResultado.equals("tNatural")||tipoResultado.equals("tEntero")){
			int res = Integer.valueOf(v1) * Integer.valueOf(v2);
			return new Registro(Integer.toString(res),tipoResultado);
		}
		else {
			double res = Double.parseDouble(v1) * Double.parseDouble(v2); 
			return new Registro(Double.toString(res),tipoResultado);
		}	
	}

	private void procesaDivide() throws Exception {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "divide");
		Registro resultado = divide(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro divide(String v1, String v2, String tipoResultado) throws Exception {
		if(tipoResultado.equals("tNatural")||tipoResultado.equals("tEntero")){
			if(Integer.valueOf(v2)==0) throw new Exception("Error en ejecucion: Division entre cero");
			int res = Integer.valueOf(v1) / Integer.valueOf(v2);
			return new Registro(Integer.toString(res),tipoResultado);
		}
		else {
			if(Double.parseDouble(v2)==0) throw new Exception("Error en ejecucion: Division entre cero");
			double res = Double.parseDouble(v1) / Double.parseDouble(v2); 
			return new Registro(Double.toString(res),tipoResultado);
		}	
	}

	private void procesaMenor() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "menor");
		Registro resultado = menor(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
		
	}

	private Registro menor(String v1, String v2, String tipoComparacion) {
		boolean res;
		if(tipoComparacion.equals("tNatural")||tipoComparacion.equals("tEntero")){
			 res = Integer.valueOf(v1) < Integer.valueOf(v2);
		}
		else if(tipoComparacion.equals("tReal")){
			res = Double.parseDouble(v1) < Double.parseDouble(v2); 
		}	
		else if(tipoComparacion.equals("tCaracter")){
			res = v1.charAt(0) < v2.charAt(0); 
		}
		else{
			if(v2.equals("verdadero")&&v1.equals("falso")){
				res = true;
			}
			else res = false;
		}
		if(res)	return new Registro("verdadero","tBooleano");
		return new Registro("falso","tBooleano");
	}

	private void procesaMayor() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "mayor");
		Registro resultado = mayor(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
		
	}

	private Registro mayor(String v1, String v2, String tipoComparacion) {
		boolean res;
		if(tipoComparacion.equals("tNatural")||tipoComparacion.equals("tEntero")){
			 res = Integer.valueOf(v1) > Integer.valueOf(v2);
		}
		else if(tipoComparacion.equals("tReal")){
			res = Double.parseDouble(v1) > Double.parseDouble(v2); 
		}	
		else if(tipoComparacion.equals("tCaracter")){
			res = v1.charAt(0) > v2.charAt(0); 
		}
		else{
			if(v1.equals("verdadero")&&v2.equals("falso")){
				res = true;
			}
			else res = false;
		}
		if(res)	return new Registro("verdadero","tBooleano");
		return new Registro("falso","tBooleano");
	}

	private void procesaMenorIgual() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "menorIgual");
		Registro resultado = menorIgual(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro menorIgual(String v1, String v2, String tipoComparacion) {
		boolean res;
		if(tipoComparacion.equals("tNatural")||tipoComparacion.equals("tEntero")){
			 res = Integer.valueOf(v1) <= Integer.valueOf(v2);
		}
		else if(tipoComparacion.equals("tReal")){
			res = Double.parseDouble(v1) <= Double.parseDouble(v2); 
		}	
		else if(tipoComparacion.equals("tCaracter")){
			res = v1.charAt(0) <= v2.charAt(0); 
		}
		else{
			if(v1.equals("verdadero")&&v2.equals("falso")){
				res = false;
			}
			else res = true;
		}
		if(res)	return new Registro("verdadero","tBooleano");
		return new Registro("falso","tBooleano");
	}

	private void procesaMayorIgual() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "mayorIgual");
		Registro resultado = mayorIgual(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro mayorIgual(String v1, String v2, String tipoComparacion) {
		boolean res;
		if(tipoComparacion.equals("tNatural")||tipoComparacion.equals("tEntero")){
			 res = Integer.valueOf(v1) >= Integer.valueOf(v2);
		}
		else if(tipoComparacion.equals("tReal")){
			res = Double.parseDouble(v1) >= Double.parseDouble(v2); 
		}	
		else if(tipoComparacion.equals("tCaracter")){
			res = v1.charAt(0) >= v2.charAt(0); 
		}
		else{
			if(v2.equals("verdadero")&&v1.equals("falso")){
				res = false;
			}
			else res = true;
		}
		if(res)	return new Registro("verdadero","tBooleano");
		return new Registro("falso","tBooleano");
	}

	private void procesaIgualIgual() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "igualIgual");
		Registro resultado = igualIgual(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro igualIgual(String v1, String v2, String tipoComparacion) {
		boolean res;
		if(tipoComparacion.equals("tNatural")||tipoComparacion.equals("tEntero")){
			 res = Integer.valueOf(v1) == Integer.valueOf(v2);
		}
		else if(tipoComparacion.equals("tReal")){
			res = Double.parseDouble(v1) == Double.parseDouble(v2); 
		}	
		else if(tipoComparacion.equals("tCaracter")){
			res = v1.charAt(0) == v2.charAt(0); 
		}
		else{
			if((v2.equals("verdadero")&&v1.equals("verdadero"))||(v2.equals("falso")&&v1.equals("falso"))){
				res = true;
			}
			else res = false;
		}
		if(res)	return new Registro("verdadero","tBooleano");
		return new Registro("falso","tBooleano");
	}

	private void procesaDistinto() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "distinto");
		Registro resultado = distinto(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro distinto(String v1, String v2, String tipoComparacion) {
		boolean res;
		if(tipoComparacion.equals("tNatural")||tipoComparacion.equals("tEntero")){
			 res = Integer.valueOf(v1) != Integer.valueOf(v2);
		}
		else if(tipoComparacion.equals("tReal")){
			res = Double.parseDouble(v1) != Double.parseDouble(v2); 
		}	
		else if(tipoComparacion.equals("tCaracter")){
			res = v1.charAt(0) != v2.charAt(0); 
		}
		else{
			if((v2.equals("verdadero")&&v1.equals("verdadero"))||(v2.equals("falso")&&v1.equals("falso"))){
				res = false;
			}
			else res = true;
		}
		if(res)	return new Registro("verdadero","tBooleano");
		return new Registro("falso","tBooleano");
	}

	private void procesaModulo() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "modulo");
		Registro resultado = modulo(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro modulo(String v1, String v2, String tipoResultado) {
		int res = Integer.valueOf(v1) % Integer.valueOf(v2);
		return new Registro(Integer.toString(res),tipoResultado);
	}

	private void procesaY() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "y");
		Registro resultado = y(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}
	
	private Registro y(String v1, String v2, String tipoComparacion) {
		boolean res;
		if(v2.equals(v1) && v2.equals("verdadero")) res = true;
		else res = false;
		if(res)	return new Registro("verdadero","tBooleano");
		return new Registro("falso","tBooleano");
	}

	private void procesaO() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		String tipoResultado = dameTipoResultado(r1.dameTipo(), r2.dameTipo(), "o");
		Registro resultado = o(r1.dameValor(),r2.dameValor(), tipoResultado);
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro o(String v1, String v2, String tipoResultado) {
		boolean res;
		if(v2.equals(v1) && v2.equals("falso")) res = false;
		else res = true;
		if(res)	return new Registro("verdadero","tBooleano");
		return new Registro("falso","tBooleano");
	}
	
	private void procesaNo() {
		Registro r;
		if(pila.damePila(pila.damecPila()).dameValor().equals("verdadero"))
			r = new Registro("falso",pila.damePila(pila.damecPila()).dameTipo());
		else r = new Registro("verdadero",pila.damePila(pila.damecPila()).dameTipo());	
		pila.ponPila(pila.damecPila(), r);
		cProg ++;		
	}

	private void procesaCSigno() {
		Registro r;
		String aux = pila.damePila(pila.damecPila()).dameValor();
		aux = "-" + aux;
		if (pila.damePila(pila.damecPila()).dameTipo().equals("tReal")){
			r = new Registro(aux,"tReal");
		}		
		else r = new Registro(aux,"tEntero");	
		pila.ponPila(pila.damecPila(), r);
		cProg ++;	
	}

	private void procesaDesplIzquierda() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		Registro resultado = desplIzquierda(r1.dameValor(),r2.dameValor());
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro desplIzquierda(String v1, String v2) {
		int  res = Integer.valueOf(v1) << Integer.valueOf(v2);
		return new Registro(Integer.toString(res), "tNatural");
	}

	private void procesaDesplDerecha() {
		Registro r1 = new Registro(pila.damePila(pila.damecPila()-1)); 
		Registro r2 = new Registro(pila.damePila(pila.damecPila()));
		Registro resultado = desplDerecha(r1.dameValor(),r2.dameValor());
		pila.ponPila(pila.damecPila()-1, resultado);
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}

	private Registro desplDerecha(String v1, String v2) {
		int  res = Integer.valueOf(v1) >> Integer.valueOf(v2);
		return new Registro(Integer.toString(res), "tNatural");
	}

	private void procesaCastNat() {
		if(pila.damePila(pila.damecPila()).dameTipo().equals("tCaracter")){
			int res = (int)pila.damePila(pila.damecPila()).dameValor().charAt(0);
			Registro resultado = new Registro(Integer.toString(res),"tNatural");
			pila.ponPila(pila.damecPila(), resultado);
		}
		cProg ++;
	}

	private void procesaCastEnt() {
		if(pila.damePila(pila.damecPila()).dameTipo().equals("tCaracter")){
			int res = (int)pila.damePila(pila.damecPila()).dameValor().charAt(0);
			Registro resultado = new Registro(Integer.toString(res),"tEntero");
			pila.ponPila(pila.damecPila(), resultado);
		}
		else if(pila.damePila(pila.damecPila()).dameTipo().equals("tReal")){
			String[] aux = pila.damePila(pila.damecPila()).dameValor().split(".");
			Registro resultado = new Registro(aux[0],"tEntero");
			pila.ponPila(pila.damecPila(), resultado);
		}
		else if(pila.damePila(pila.damecPila()).dameTipo().equals("tNatural")){
			String aux = pila.damePila(pila.damecPila()).dameValor();
			Registro resultado = new Registro(aux,"tEntero");
			pila.ponPila(pila.damecPila(), resultado);
		}
		cProg ++;
	}

	private void procesaCastReal() {
		if(pila.damePila(pila.damecPila()).dameTipo().equals("tCaracter")){
			int res = (int)pila.damePila(pila.damecPila()).dameValor().charAt(0);
			Registro resultado = new Registro(Integer.toString(res)+".0","tReal");
			pila.ponPila(pila.damecPila(), resultado);
		}
		else if(pila.damePila(pila.damecPila()).dameTipo().equals("tNatural")||
				pila.damePila(pila.damecPila()).dameTipo().equals("tEntero")){
			Registro resultado = new Registro(pila.damePila(pila.damecPila()).dameValor()+".0","tReal");
			pila.ponPila(pila.damecPila(), resultado);
		}
		cProg ++;
	}

	private void procesaCastCar() {
		if(pila.damePila(pila.damecPila()).dameTipo().equals("tNatural")){
			char res = (char)pila.damePila(pila.damecPila()).dameValor().charAt(0);
			Registro resultado = new Registro(Character.toString(res),"tCaracter");
			pila.ponPila(pila.damecPila(), resultado);
		}
		cProg ++;
	}
	
	private void procesaEscribe() {
		Registro r = pila.damePila(pila.damecPila());
		System.out.println("SALIDA >> "+r.dameValor());
		pila.poncPila(pila.damecPila()-1);
		cProg ++;
	}
	
	private String dameTipoResultado(String tipo1, String tipo2, String operacion) {
		if(operacion.equals("suma")||operacion.equals("resta")||operacion.equals("multiplica")||
				operacion.equals("divide")){
			if(tipo1.equals("tNatural")){
				if(tipo2.equals("tNatural")) return "tNatural";
				else if(tipo2.equals("tEntero")) return "tEntero";
				else return "tReal";
			}			
			else if(tipo1.equals("tEntero")){
				if(tipo2.equals("tNatural")) return "tEntero";
				else if(tipo2.equals("tEntero")) return "tEntero";
				else return "tReal";
			}
			else return "tReal";
		}
		else if(operacion.equals("menor")||operacion.equals("mayor")||operacion.equals("menorIgual")||
			operacion.equals("mayorIgual")||operacion.equals("igualIgual")||operacion.equals("distinto")){
			if(tipo1.equals("tBooleano"))return "tBooleano";
			else if(tipo1.equals("tCaracter")) return "tCaracter";
			else if(tipo1.equals("tReal")||tipo2.equals("tReal")) return "tReal";
			else return "tEntero";
		}
		else if(operacion.equals("modulo")){
			if(tipo1.equals("tEntero")) return "tEntero";
			else return "tNatural";
		}
		else return "tBooleano";
	}

	private String dameTipo(String numAux) {
		if(numAux.contains("'")) return "tChar";
		else if(numAux.equals("verdadero")||numAux.equals("falso")) return "tBooleano";
		else if(numAux.contains(".")||numAux.contains("E")||numAux.contains("e")) return "tReal";
		else return "tNatural";
	}
	
	private void siguienteInstruccion() throws IOException {
		instruccion =memoriaI.getI(cProg); 
	}
	
	private void imprime() {
		System.out.println("********************************************");
		System.out.println("PASO "+cProg+", INSTRUCCION: "+instruccion+"\n");
		System.out.println("Contenido de la pila:");
		pila.imprime();
		System.out.println("Contenido de la memoria:");
		memoria.imprime();
	}

}
